<?php

declare(strict_types=1);

namespace App\Helpers;

/**
 * 杉德支付
 */
class SandPayUtil
{
    const API_HOST = 'https://caspay.sandpay.com.cn/agent-main/openapi/';
    const PUB_KEY_PATH = __DIR__ . "/../../config/sand_cert/sand.cer"; //！！！ 公钥文件，这个不要改动
    const PRI_KEY_PATH = __DIR__ . "/../../config/sand_cert/6888802043619.pfx"; //私钥文件，要改为商户自己的私钥
    const CERT_PWD = 'hhgs_520'; //私钥证书密码，要改为商户自己的私钥密码

    /**
     * 充值下单
     */
    static function orderCreate($orderInfo = [], $channel = '')
    {
     
	return false;
	$orderId = $orderInfo['out_trade_no'] ?? date('YmdHis') . '_' . mt_rand(1000, 9999);
        switch ($channel) {
            case 'wechat':
                $productNo = '02010005';
                $appId = ConfigUtil::getWechatPayInfo('app_id');
                $payInfo = [
                    "wx_app_id" => $appId,
                    "gh_ori_id" => "gh_0a5b14895f9e",
                    "path_url" => "pages/zf/index?",
                    "miniProgramType" => "0",
                ];
                break;
            case 'alipay':
                $productNo = '02020004';
                $payInfo = ["buyer_id" => ""];
                break;
        }
        $data = [
            'version' => '10',
            'mer_no' => config('pay.sand.mer_no'),
            'mer_key' => config('pay.sand.mer_key'),
            'mer_order_no' => $orderId,
            'create_time' => date('YmdHis'),
            'order_amt' => (string)round($orderInfo['total_amount'] / 100, 2),
            'notify_url' => config('pay.sand.notify_url'),
            'create_ip' => str_replace('.', '_', $orderInfo['client_ip']),
            'store_id' => '000000',
            'pay_extra' => json_encode($payInfo),
            'accsplit_flag' => 'NO',
            'sign_type' => 'MD5',
        ];
        ksort($data);
        $md5Key = config('pay.sand.md5_key');
        $str = urldecode(http_build_query($data)) . '&key=' . $md5Key;
        log_user_result($orderId, $str);
        $data['sign'] = strtoupper(md5($str));
        $subject = $orderInfo['subject'] ?? '';
        $data['expire_time'] = date('YmdHis', time() + 60);
        $data['goods_name'] = $subject;
        $data['product_code'] = $productNo;
        $data['return_url'] = '';
        $data['clear_cycle'] = '0';
        $data['jump_scheme'] = 'sandcash://scpay';
        return $data;
    }

    /**
     * 实时代付
     */
    static function agentPay($orderInfo = [])
    {
        if (!isset($orderInfo['total_amount']) || $orderInfo['total_amount'] <= 1) return false;
        $info = [
            'transCode' => 'RTPM', // 实时代付
            'merId' => config('pay.sand.mer_no'), // 此处更换商户号
            'url' => '/agentpay',
            'pt' => [
                'orderCode' => date('YmdHis') . mt_rand(1000, 9999),
                'version' => '01',
                'productId' => '00000004',
                'tranTime' => date('YmdHis', time()),
                'timeOut' => date('YmdHis', time() + 60),
                'tranAmt' => sprintf("%012d", $orderInfo['total_amount']),
                'currencyCode' => '156',
                'accAttr' => '0',
                'accNo' => '6214868651890625',
                'accType' => '4',
                'accName' => '张平安',
                'bankName' => 'cmb',
                'remark' => '极速', // 这个字段不要出现“代付”的字样
                'payMode' => '2',
                'channelType' => '07'
            ]
        ];

        // step1: 拼接报文及配置
        $transCode = $info['transCode']; // 交易码
        $accessType = '0'; // 接入类型 0-商户接入，默认；1-平台接入
        $merId = $info['merId']; // 此处更换商户号
        $path = $info['url']; // 服务地址
        $pt = $info['pt']; // 报文

        // step2: 生成AESKey并使用公钥加密
        $AESKey = self::aesGenerate(16);

        // 获取公私钥匙
        $pubKey = self::loadX509Cert(self::PUB_KEY_PATH);
        $encryptKey = self::rsaEncryptByPub($AESKey, $pubKey);

        // step3: 使用AESKey加密报文
        $encryptData = self::aesEncrypt($pt, $AESKey);

        // step4: 使用私钥签名报文
        $priKey = self::loadPk12Cert(self::PRI_KEY_PATH, self::CERT_PWD);
        $sign = self::sign($pt, $priKey);

        // step5: 拼接post数据
        $post = [
            'transCode' => $transCode,
            'accessType' => $accessType,
            'merId' => $merId,
            'encryptKey' => $encryptKey,
            'encryptData' => $encryptData,
            'sign' => $sign
        ];

        // step6: post请求
        $result = self::httpPostJson(self::API_HOST . $path, $post);
        parse_str($result, $arr);
        try {
            // step7: 使用私钥解密AESKey
            $decryptAESKey = self::rsaDecryptByPri($arr['encryptKey'], $priKey);

            // step8: 使用解密后的AESKey解密报文
            $decryptPlainText = self::aesDecrypt($arr['encryptData'], $decryptAESKey);

            // step9: 使用公钥验签报文
            self::verify($decryptPlainText, $arr['sign'], $pubKey);
        } catch (\Exception $e) {
            return ['errcode' => -1, 'errmsg' => $e->getMessage()];
        }
        // print_r($decryptPlainText);
        return ['errcode' => 0, 'data' => $decryptPlainText];
    }

    /**
     * 获取公钥
     * @param $path
     * @return mixed
     * @throws Exception
     */
    static function loadX509Cert($path)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new \Exception('loadx509Cert::file_get_contents ERROR');
            }

            $cert = chunk_split(base64_encode($file), 64, "\n");
            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";

            $res = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);

            if (!$detail) {
                throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
            }

            return $detail['key'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 获取私钥
     * @param $path
     * @param $pwd
     * @return mixed
     * @throws Exception
     */
    static function loadPk12Cert($path, $pwd)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new \Exception('loadPk12Cert::file
					_get_contents');
            }

            if (!openssl_pkcs12_read($file, $cert, $pwd)) {
                throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
            }
            return $cert['pkey'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 私钥签名
     * @param $plainText
     * @param $path
     * @return string
     * @throws Exception
     */
    static function sign($plainText, $path)
    {
        $plainText = json_encode($plainText);
        try {
            $resource = openssl_pkey_get_private($path);
            $result = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);

            if (!$result) {
                throw new \Exception('签名出错' . $plainText);
            }

            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 公钥验签
     * @param $plainText
     * @param $sign
     * @param $path
     * @return int
     * @throws Exception
     */
    static function verify($plainText, $sign, $path)
    {
        $resource = openssl_pkey_get_public($path);
        $result = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);

        if (!$result) {
            throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
        }

        return $result;
    }

    /**
     * 公钥加密AESKey
     * @param $plainText
     * @param $puk
     * @return string
     * @throws Exception
     */
    static function rsaEncryptByPub($plainText, $puk)
    {
        if (!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)) {
            throw new \Exception('AESKey 加密错误');
        }
        return base64_encode($cipherText);
    }

    /**
     * 私钥解密AESKey
     * @param $cipherText
     * @param $prk
     * @return string
     * @throws Exception
     */
    static function rsaDecryptByPri($cipherText, $prk)
    {
        if (!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)) {
            throw new \Exception('AESKey 解密错误');
        }
        return (string)$plainText;
    }

    /**
     * AES加密
     * @param $plainText
     * @param $key
     * @return string
     * @throws \Exception
     */
    static function aesEncrypt($plainText, $key)
    {
        $plainText = json_encode($plainText);
        $result = openssl_encrypt($plainText, 'AES-128-ECB', $key, 1);

        if (!$result) {
            throw new \Exception('报文加密错误');
        }

        return base64_encode($result);
    }

    /**
     * AES解密
     * @param $cipherText
     * @param $key
     * @return string
     * @throws \Exception
     */
    static function aesDecrypt($cipherText, $key)
    {
        $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);

        if (!$result) {
            throw new \Exception('报文解密错误', 2003);
        }

        return $result;
    }

    /**
     * 生成AESKey
     * @param $size
     * @return string
     */
    static function aesGenerate($size)
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $arr = array();
        for ($i = 0; $i < $size; $i++) {
            $arr[] = $str[mt_rand(0, 61)];
        }

        return implode('', $arr);
    }

    /**
     * 发送请求
     * @param $url
     * @param $param
     * @return bool|mixed
     * @throws Exception
     */
    static function httpPostJson($url, $param)
    {
        if (empty($url) || empty($param)) {
            return false;
        }
        $param = http_build_query($param);
        try {

            $ch = curl_init(); //初始化curl
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            //正式环境时解开注释
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data = curl_exec($ch); //运行curl
            curl_close($ch);

            if (!$data) {
                throw new \Exception('请求出错');
            }

            return $data;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // // 异步通知通用接口
    // public function notify()
    // {
    //     // 实例化客户端
    //     $client = new SandH5Cashier;

    //     $sign = $_POST['sign']; //签名
    //     $data = stripslashes($_POST['data']); //支付数据

    //     // 验签
    //     try {
    //         $verifyFlag = $client->verify($data, $sign);
    //         if (!$verifyFlag) throw new \Exception('签名失败');
    //     } catch (\Exception $e) {
    //         exit('签名失败');
    //     }

    //     // 回调数据
    //     echo '<pre>';
    //     print_r($data);
    //     echo '</pre>';
    // }

    // public function orderMcAutoNotice()
    // {
    //     $client = new SandH5Cashier;
    //     // 参数
    //     $client->body = array(
    //         'orderCode'  => 'Y201805071725592',
    //         'noticeType' => '00',
    //     );
    //     // 返回结果
    //     $ret = $client->request('orderMcAutoNotice');
    //     echo '<pre>';
    //     print_r($ret);
    //     echo '</pre>';
    // }

    // public function clearfileDownload()
    // {
    //     // 实例化客户端
    //     $client = new SandH5Cashier;
    //     // 参数
    //     $client->body = array(
    //         'clearDate' => '20200611', // 结算日期
    //         'fileType'  => '1', // 文件返回类型
    //         'extend'    => ''
    //     );
    //     // 返回值
    //     $ret = $client->request('clearfileDownload');
    //     echo '<pre>';
    //     print_r($ret);
    //     echo '</pre>';
    // }
}
